package sketch.ounit;

import java.util.List;

import randoop.RMethod;
import sketch.ounit.fuzz.AssembledSequenceObjectCreation;
import sketch.ounit.fuzz.AssembledSequenceObjectCreationWithArgs;
import sketch.ounit.fuzz.AssembledSequenceObjectMutation;
import sketch.ounit.fuzz.AssembledSequenceObjectMutationWithArgs;
import sketch.ounit.fuzz.ExistingObjectFuzzer;
import sketch.ounit.fuzz.NewObjectFuzzer;
import sketch.ounit.fuzz.SharedSequencePool;
import sketch.util.Checker;

public class Values {
	
	public static boolean throw_exception = false;
	
	
	public static String random_fuzz_class = "Values";
	public static String random_fuzz_full_class = "sketch.ounit.Values";
	
	public static String choose = "choose";
	public static String exhaust = "exhaust";
	public static String subset = "subset";
	
	/**
	 * Value choosing APIs
	 * */
	//choose one value from the given ones
	public static<T> T choose(T... values) {
		//return null;
		if(throw_exception) {
		  throw new UnsupportedOperationException("choose not supported");
		}
		return null;
	}
	//pick a value randomly
	public static<T> T[] randomChoose(T...values) {
		if(throw_exception) {
		  throw new UnsupportedOperationException("randomChoose not supported");
		}
		return null;
	}
	//exhaustively choose a subset
	//here the order does not matter
	public static <T> T[] subset(T...values) {
		if(throw_exception) {
		  throw new UnsupportedOperationException("subset not supported");
		}
		return null;
	}
	//subset, but the order in each element matters matters
	public static <T> T[] exhaust(T...values) {
		if(throw_exception) {
		  throw new UnsupportedOperationException("can not invoke this directly");
		}
		return null;
	}
	
	/***
	 * For integrating symbolic execution
	 */
	public static boolean symbolicBoolean() {
		//when interpreting the result 1 -> true,  0 -> false
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicBoolean() directly.");
		}
		return false;
	}
	public static int symbolicInt() {
		//totally ok
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicInt() directly.");
		}
		return 0;
	}
	public static long symbolicLong() {
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicLong() directly.");
		}
		return 0;
	}
	public static float symbolicFloat() {
		//convert to double
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicFloat() directly.");
		}
		return 0.0f;
	}
	public static double symbolicDouble() {
		//directly use double
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicDouble() directly.");
		}
		return 0.0d;
	}
	//symref
	public static short symbolicShort() {
		//what does symref mean?
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicShort() directly.");
		}
		return 0;
	}
	public static char symbolicChar() {
		//the same as short use symref
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicChar() directly.");
		}
		return '0';
	}
	public static byte symbolicByte() {
		//the same as char and short, use symref
		//ref is a part of integer in JPF implementation
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method symbolicByte() directly.");
		}
		return 0;
	}
	
	
	/***
	 * The following two methods are doing the same thing
	 * #FIXME Note, others are not used in this method! it will be fetched for pre-processing
	 */
	public static<T> T randomCreate(Class<T> clazz, Class<?>...others) {
		AssembledSequenceObjectCreation<T> obj_creation
            = new AssembledSequenceObjectCreation<T>(clazz, SharedSequencePool.getComponents());
		T result = obj_creation.assemble_and_execute_sequence();
		//add to the sequence pool
		SharedSequencePool.addCreationSequence(obj_creation);
		
		return result;
	}
	
	public static<T> T randomCreate(Class<T> clazz, Object...others) {
		AssembledSequenceObjectCreationWithArgs<T> obj_creation
            = new AssembledSequenceObjectCreationWithArgs<T>(clazz, SharedSequencePool.getComponents(),
            		SharedSequencePool.lookup_constructors(clazz), others);
		T result = obj_creation.assemble_and_execute_sequence();
		//add to the sequence pool
		SharedSequencePool.addCreationSequence(obj_creation);
		
		return result;
	}
	
	//the class others are not used, it is fetched in pre-processing
	public static<T> T randomMutate(T t, Class<T> clazz, Class<?>...others) {
		Checker.checkNull(t, "The give object for fuzzing could not be null.");
		
		List<RMethod> rmethods = SharedSequencePool.lookup_methods(clazz);
		AssembledSequenceObjectMutation<T> assembled_sequence
		    = new AssembledSequenceObjectMutation<T>(rmethods, t, clazz, SharedSequencePool.getComponents());
		assembled_sequence.execute_sequence();
		//add to the sequence pool
		SharedSequencePool.addMutationSequence(assembled_sequence);
		
		return assembled_sequence.getResultObject();
	}
	
	public static<T> T randomMutate(T t, Class<T> clazz, Object...args) {
		Checker.checkNull(t, "The give object for fuzzing could not be null.");
		
		List<RMethod> rmethods = SharedSequencePool.lookup_methods(clazz);
		AssembledSequenceObjectMutationWithArgs<T> assembled_sequence
		    = new AssembledSequenceObjectMutationWithArgs<T>(rmethods, t, clazz, SharedSequencePool.getComponents(), args);
		assembled_sequence.execute_sequence();
		
		//add to the sequence pool
		SharedSequencePool.addMutationSequence(assembled_sequence);
		
		return assembled_sequence.getResultObject();
	}
	
	/**
	 * the old API, i am going to obsolete these APIs soon
	 * #XXX the following API are hard to use, and misleading
	 * */
	public static<T> T random(Class<T> clazz, Class<?>...others) {
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method random(Class<T> clazz, Class<?>...others) directly.");
		}
		return null;
	}
	public static<T> T randoop(Class<T> clazz, Class<?>...others) {
		if(throw_exception) {
		  throw new UnsupportedOperationException("You should not call this method randoop(Class<T> clazz, Class<?>...others) directly.");
		}
		return null;
	}
	
	//fuzzing a given object t by invoking methods
	//where to store the sequence
	public static<T> T randomFuzz(T t, Class<T> clazz, Class<?>...helpClasses) {
		//given an object t, fuzz it using methods in the given class set
		if(t == null) {
			return NewObjectFuzzer.createNewObject(clazz, helpClasses);
		} else {
			return  ExistingObjectFuzzer.fuzzObject(t, clazz, helpClasses);
		}
//		throw new UnsupportedOperationException("You should not call this method randomFuzz(T t, Class<?>...classes) directly.");
	}
	
//	public static<T> T randomFuzz(T t, Class<T> clazz, Object...args) {
//		Checker.checkNull(t, "The give object for fuzzing could not be null.");
//		return ExistingObjectFuzzer.fuzzObject(t, clazz, args);
//	}
	
	public static String random_fuzz_method = "random";
	public static<T> T random(T t, Class<T> clazz, Object...args) {
		Checker.checkNull(t, "The give object for fuzzing could not be null.");
		return ExistingObjectFuzzer.fuzzObject(t, clazz, args);
	}
	
}